package session

import (
	"sync"
	"time"
)

type sessionMemory struct {
	mute	sync.RWMutex
	last	time.Time

	values	map[string]interface{}
}

func (sem *sessionMemory) Get(key string) interface{} {
	sem.mute.RLock()
	value := sem.values[key]
	sem.mute.RUnlock()

	sem.mute.Lock()
	sem.last = time.Now()
	sem.mute.Unlock()

	return value
}

func (sem *sessionMemory) Set(key string, value interface{}) {
	sem.mute.Lock()
	sem.values[key] = value
	sem.last = time.Now()
	sem.mute.Unlock()
}

func (sem *sessionMemory) Del(key string) {
	sem.mute.Lock()
	delete(sem.values, key)
	sem.last = time.Now()
	sem.mute.Unlock()
}

type StoreMemory struct {
	mute	sync.RWMutex
	expire	time.Duration
	ticker	*time.Ticker

	values	map[string]*sessionMemory
}

func NewStoreMemory() *StoreMemory {
	return &StoreMemory{
		values: make(map[string]*sessionMemory),
	}
}

func (sm *StoreMemory) SetExpire(expire time.Duration) {
	sm.expire = expire
}

func (sm *StoreMemory) newSessionMemory() *sessionMemory {
	return &sessionMemory{
		values: make(map[string]interface{}),
	}
}

func (sm *StoreMemory) Get(id string, key string) interface{} {
	sm.mute.Lock()
	defer sm.mute.Unlock()

	value, ok := sm.values[id]
	if !ok {
		return nil
	}

	if sm.expire > 0 && time.Now().Sub(value.last) > sm.expire {
		delete(sm.values, id)
		return nil
	}

	return value.Get(key)
}

func (sm *StoreMemory) Set(id string, key string, value interface{}) error {
	sm.mute.Lock()
	v, ok := sm.values[id]
	if !ok {
		v = sm.newSessionMemory()
		sm.values[id] = v
	}

	sm.mute.Unlock()

	if len(key) > 0 && value != nil {
		v.Set(key, value)
	}

	return nil
}

func (sm *StoreMemory) Del(id string, key string) error {
	sm.mute.Lock()
	if value, ok := sm.values[id]; ok {
		value.Del(key)
	}
	sm.mute.Unlock()
	return nil
}

func (sm *StoreMemory) Clear(id string) error {
	sm.mute.Lock()
	defer sm.mute.Unlock()
	delete(sm.values, id)
	return nil
}

func (sm *StoreMemory) Exist(id string) bool {
	sm.mute.RLock()
	defer sm.mute.RUnlock()
	_, ok := sm.values[id]
	return ok
}

func (sm *StoreMemory) Start() error {
	loop := func() {
		delay := time.Now().Add(-sm.expire)
		for k, v := range sm.values {
			if v.last.Before(delay) {
				delete(sm.values, k)
			}
		}
	}

	sm.ticker = time.NewTicker(sm.expire)
	go func() {
		for range sm.ticker.C {
			loop()
		}
	}()

	return nil
}